#===============================================================================
# Generic complexity calculator
#===============================================================================
COMPLEXITY_FUNCS = {}
def register_type(*types):
    '''Decorates a function as the complexity calculator for the given type'''

    def decorator(func):
        for tt in types:
            COMPLEXITY_FUNCS[tt] = func
        return func
    return decorator

_round = round
def complexity(obj, method='default', round=True):
    '''Compute the complexity of the given object using the selected method.'''

    tt = type(obj)

    # Tries to use a registered implementation
    try:
        implementation = COMPLEXITY_FUNCS[tt]
    except KeyError:
        pass
    else:
        result = implementation(obj, method)

    # Look for an implementation that fulfill some of its bases 
    for super_tt in tt.mro():
        if super_tt in COMPLEXITY_FUNCS:
            implementation = COMPLEXITY_FUNCS[tt] = COMPLEXITY_FUNCS[super_tt]
            result = implementation(obj, method)
            break
    else:
        tname = tt.__name__
        raise TypeError("cannot compute complexity for '%s' objects" % tname)

    if round:
        result = _round(result)
    return result

complexity.register = register_type
